<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>

	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
		<title>Insert title here</title>
	</head>

	<body>
		<h1>类和模块</h1>
		<p>
			原型对象是类的唯一标识(当且仅当两个对象继承自同一个原型对象时,它们才属于同一个类的实例)<br>
			r instanceof Range<br>
			instanceof运算符并不会检查r是否是由Range()构造函数初始化而来,而会检查r是否继承自Range.prototype<br>
			每个js函数都有一个prototype属性,这个属性的值是一个对象,这个对象包含唯一一个不可枚举的属性constructor,其值也是一个对象<br>
			基于原型的继承机制是动态的,也就是创建对象之后对原型的修改会影响到已创建的对象,因此可以给很多原型添加方法
			例如：Object.prototype(不建议这么干), Function.prototype(添加对象方法), HTMLElement.prototype(IE不行)
			？？？鸭式辩型<br>
			Set集合<br>
			枚举<br>
			私有状态,将变量闭包在一个构造函数内来实现私有实例字段,性能会有影响<br>
			构造函数的重载和工厂方法 <br>
			子类:js中创建子类关键在于对原型对象进行初始化。类继承的替代方案:组合<br>
			构造函数和方法链<br>
			<b>继承的模板：</b>
			function Subclass(){  //构造函数
				Superclass.apply(this,arguments);
			}
			Subclass.prototype = inherit(Superclass.prototype);
			Subclass.prototype.constructor = Subclass;<br>
			面向对象编程的一条设计原则：组合优于继承<br>
			属性不可枚举,不可变类,Object.defineProperty、Object.defineProperties()和Object.create()<br>
			封装对象状态，防止类的扩展，属性描述符,模块<br>
			用作命名空间的对象<br>
			
			
		</p>
		<script type="text/javascript">
			
			function inherit(p){
				if(p == null) throw TypeError();
				if(Object.create) return Object.create(p);
				var t = typeof p;
				if(t!=="object" && t!=="function") throw TypeError();
				function f(){};
				f.prototype = p;
				return new f();
			}
			function range(from, to){
				//原型对象作为函数的一个属性存储
				var r = inherit(range.methods);
				r.from = from;
				r.to = to;
				return r;
			}
			//原型对象定义方法
			range.methods = {
				includes : function(x){return this.from<=x && x<=this.to;},
				foreach : function(f){for(var x=Math.ceil(this.from);x<=this.to;x++) f(x);},
				toString : function(){return "(" + this.from +"..."+this.to + ")";}
			};
			var obj1 = range("1","3");
			console.log("includes:"+obj1.includes("2"));//true
			obj1.foreach(console.log);//1,2,3
			console.log(obj1);
			
			//通过构造函数的方式
			function Range(from, to){
				this.from = from;
				this.to = to;
			}
			//重新定义prototype后,这个原型对象就不在含有constructor属性了
			Range.prototype = {
				constructor:Range,//方法一：显示设置构造函数反向引用 
				includes : function(x){return this.from<=x && x<=this.to;},
				foreach : function(f){for(var x=Math.ceil(this.from);x<=this.to;x++) f(x);},
				toString : function(){return "(" + this.from +"..."+this.to + ")";}
			};
			//方法二：使用预定义原型对象,逐个定义
			//Range.prototype.includes = function(x){...}
			//Range.prototype.foreach = function(f){...}
			//Range.prototype.toString = function(){...}
			var obj2 = new Range("3","5");
			console.log("includes:"+obj2.includes("4"));//true
			obj2.foreach(console.log);//1,2,3
			console.log(obj2);//
			obj2.from = 4;
			console.log(obj2.toString());
			//---
			function F(){}
			var p = F.prototype;
			var c = p.constructor;
			console.log(c===F);//true
			//---
			function extend(o,p){
				for(prop in p){
					o[prop] = p[prop];
				}
				return o;
			}
			//加强版extend
// 			var extend = (function(){
// 				//需要检查的特殊属性
// 				var prototypes = ["toString","valueOf","constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocalString"];
// 				//处理bug[ie中,如果对象o的属性中有一个不可枚举的同名属性,则for/in不会枚举对象o的可枚举属性,也就是不会正确处理诸如toString的属性]
// 				for(var p in {toString:null}){
// 					//至此,返回简单版本的extend
// 					return function extend(o){
// 						for(var i=1;i<arguments.length;i++){
// 							var source = arguments[i];
// 							for(var prop in source) o[prop] = source[prop];
// 						}
// 						return o;
// 					};
// 				}
// 				//如果for/in不会枚举测试对象toString,返回另一个版本的extend
// 				return function patched_extend(o){
// 					for(var i=1;i<arguments.length;i++){
// 						var source = arguments[i];
// 						for(var prop in source) o[prop] = source[prop];
// 						for(var j=0;j<prototypes.length;j++){
// 							prop = prototypes[j];
// 							if(source.hasOwnProperty(prop))
// 								o[prop] = source[prop];
// 						}
// 					};
// 					return o;
// 				};
// 			}());
			
			function defineClass(constructor,methods,statics){
				if(methods) extend(constructor.prototype,methods);
				if(statics) extend(constructor,statics);
				return constructor;
			}
			var SimpleRange = defineClass(
				function(f,t){this.f = f;this.t = t;},
				{
					includes:function(x){return this.f<=x && x<=this.t;},
					toString:function(){return this.f+"..."+this.t;}
				},
				{upto:function(t){return new SimpleRange(0,t);}}
			);
			var sr = new SimpleRange(1,3);
			console.log("includes:"+sr.includes("2"));//true
			
			//---
			function Complex(real, imaginary){
				if(isNaN(real) || isNaN(imaginary))
					throw new TypeError();
				//成员[实例]变量
				this.r = real;
				this.i = imaginary;
			}
			//成员[实例]方法
			Complex.prototype.add = function(that){return new Complex(this.r+that.r,this.i+that.i);};
			Complex.prototype.toString = function(){return "{"+this.r+","+this.i+"}";};
			//类字段,[常量],一般使用全大写字母
			Complex.ZERO = new Complex(0,0);
			//一般用下划线表示的表示内部使用,不属于类的公共的API
			Complex._format = /^\{([^,]+),([^}]+)\}$/;
			
			//类方法:将实例对象的toString返回的字符串解析为一个Complex对象 
			Complex.parse = function(s){
				try{
					var m = Complex._format.exec(s);
					return new Complex(parseFloat(m[1]),parseFloat(m[2]));
				}catch(x){
					throw new TypeError("Can't parse " + s +" as a complex number.");
				};
			};
			var c1 = new Complex(2,3);
			var c2 = new Complex(c1.i,c1.r);
			console.log("c1: "+c1.toString());
			console.log("c2: "+c2.toString());
			console.log("c1+c2: "+c1.add(c2).toString());
			console.log("parse:"+Complex.parse(c1.toString()).toString());
			/*exec的用法 
				
				var str = "Visit W3School"; 
				var patt = new RegExp("W3School","g");
				var result;
				while ((result = patt.exec(str)) != null)  {
				  document.write(result);
				  document.write("<br />");
				  document.write(result.lastIndex);
				}
				W3School
				14
			*/
			console.log(c1 instanceof Complex); //true
			console.log(Complex.isPrototypeOf(c1)); //false 
			function typeAndValue(x){
				if(x == null) return "";//Null和undefined没有构造函数 
				switch(x.constructor){
					case Number: return "Nubmer: "+x;
					case String: return "String: "+x;
					case Date: return "Date: "+x;
					case RegExp: return "RegExp: "+x;
					case Complex: return "Complex: "+x;
				}
			}
			console.log(typeAndValue(c1));
			console.log(typeAndValue(new Date()));
			//---
			function classof(o){
				if(o===null) return "Null";
				if(o===undefined) return "Undefined";
				return Object.prototype.toString.call(o).slice(8,-1);
			};
			Function.prototype.getName = function(){
				if("name" in this) return this.name;
				return this.name = this.toString().match(/function\s*([^(]*)\(/)[1];
			};
			function type(o){
				var t,c,n; //type,class,name 
				if(o===null) return "null";
				if(o!==o) return "nan";
				if((t=typeof o) !== "object") return t;
				if((c=classof(o)) !== "Object") return c;
				if(o.constructor && typeof o.constructor==="function" && (n=o.constructor.getName())) return n;
				return "Object";
			};
			console.log(type(c1));
			//鸭式辩型函数
			function quacks(o){
				for(var i=1;arguments.length;i++){
					var arg = arguments[i];
					switch(typeof arg){
						case "string":
							if(typeof o[arg]!=="function") return false;
							continue;
						case "function":
							arg = arg.prototype;
						case "object":
							for(var m in arg){
								if(typeof arg[m]!=="function") continue;
								if(typeof o[m]!=="function") return false;
							}
					}
				}
				return true;
			}
			//私有状态-这种使用闭包的封装的类肯定比不用闭包封装的运行速度更慢,并占用更多内存
			function NewRange(from,to){
				this.from = function(){return from;};
				this.to = function(){return to;};
			}
			//此时原型无法直接操作属性,必须调用存取器方法
			NewRange.prototype = {
				constructor:Range,//方法一：显示设置构造函数反向引用 
				includes : function(x){return this.from()<=x && x<=this.to();},
				foreach : function(f){for(var x=Math.ceil(this.from());x<=this.to();x++) f(x);},
				toString : function(){return "(" + this.from() +"..."+this.to() + ")";}
			};
			var nr = new NewRange(1,3);
			console.log(nr.toString());
// 			nr.to = 11; //无法直接修改
			nr.from = function(){return 0;}; //还是可以通过方法来修改 - - 
			console.log(nr.toString());
			//继承
			function NonNullSet(){
				Set.apply(this,arguments);
			};
			NonNullSet.prototype = Set.prototype;
			NonNullSet.prototype.constructor = NonNullSet;
			NonNullSet.prototype.add = function(){
				for(var i=0;i<arguments.length;i++){
					if(arguments[i] == null){
						throw new Error("Can't add null or undefined...");
					}
				}
				//调用父类add方法
				return Set.prototype.add.apply(this, arguments);
			};
			//用Function.prototype.extend()[这是创建子类的快捷方式]方法来重写NonNullSet
			var NonNullSet = (function(){
				var superclass = Set;
				return superclass.extend(
					function(){superclass.apply(this,arguments);},
					{
						add:function(){
							for(var i=0;i<arguments.length;i++){
								if(arguments[i]==null){
									throw new Error("Can't add null or undefined...");
								}
							};
							//调用父类add方法
							return superclass.prototype.add.apply(this,arguments);
						}
					}
				); 
			}());
			//用组合的方式实现
			var FilteredSet = Set.extend(
				function FilteredSet(set,filter){
					this.set = set;
					this.filter = filter;
				},
				{
					add:function(){
						if(this.filter){
							for(var i=0;i<arguments.length;i++){
								var v = arguments[i];
								if(!this.filter(v)){
									throw new Error("error...");
								}
							}
						}
						this.Set.add.apply(this,arguments);
						return this;
					},
					remove:function(){
						this.set.remove.apply(this.set,arguments);
						return this;
					},
					contains:function(v){return this.set.contains(v);},
					size:function(){return this.set.size();},
					foreach:function(f,c){this.set.foreach(f,c);}
				}
			);
			//创建集合,最终得到的是Set的实例,多态?
			var fs = new FilteredSet(new Set(),function(x){return x!=null;});
			
			
			//抽象方法 
			function abstractmethod(){throw new Error("abstrct method");}
			//抽象类
			function AbstractSet(){throw new Error("Can't instantiate abstract class...");}
			AbstractSet.prototype.contains = abstractmethod;//抽象类中的抽象方法 
			//非抽象子类
			var NotSet = AbstractSet.extend(
				function NotSet(set){this.set = set;},
				{
					contains:function(x){return !this.set.contains(x);},
					toString:function(x){return "~"+this.get.toString();},
					equals:function(that){return that instanceof NotSet && this.set.equals(that.set);}
				}
			);
			//这么一句就阻止了Object.prototype的扩展
// 			Obect.seal(Object.prototype);
			
			
			
			
		</script>
	</body>

</html>